# _*_conding:cp-1252-*-
# 教育机构：银杰教育
# 讲师：李伟
# 开发时间：2025/8/30 下午8:47

# 任何一款工具操作连接数据库步骤一致：
# 第一步：连接上数据库
#    - 连接数据库信息： ip/域名，端口，数据库名字，用户名，密码
# 第二步：需要获取游标，然后执行查询操作【sql语句】
#    - 游标：小朋友看书 手指着看过地方念文字： 读到 哪行手指向哪行。 ==手指
#    - 数据库读取数据按照游标的位置开始读。前面如果已经读完数据，后面再次读取会从游标的位置开始读取数据。
# 第三步：真正执行sql语句 获取需要的执行结果：
#    - 这个执行结果是数据的数量总数。 如果result = 0 ，说明没有符合条件的数据。
# 第四步： 获取具体的查询的数据，结果是格式保存的元组的格式保存。==不太方便，希望字典保存。？？
# - 注意问题： 游标问题
#   - cursor.fetchone() :从查询结果里获取一条数据 ==最多
#   - cursor.fetchmany(size=2): 从查询结果里获取多条数据，通过size参数进行自定义条数。
#     - 默认是元组嵌套元组，每行数据是一个元组。
#   - cursor.fetchall(): 从查询结果里获取所有的数据
# 第五步：关闭数据库连接
#   - 数据库连接数是有限制的，用完连接数用完了 后面数据库连接就连接不上。用完了数据一定要记得关闭数据库的链接和游标。
#
# 优化的问题： 获取具体的查询的数据，结果是格式保存的元组的格式保存，读取数据不太方便，希望字典保存。
# - pymysql库里直接提提供里的对应的方法： cursorclass=DictCursor  # 读取的数据默认变成字典的格式
# - 一条数据： 一个字典， {'user_phone': '13555554444', 'mobile_code': '464323'}
#    - key是数据库表的字段名，字段值作为value，形成字典的键值对。
# - 多条数据： 列表嵌套字典的格式：[{'user_phone': '13555554444', 'mobile_code': '710992'}]
#
# 虽然我们这个项目【我们以后大部分场景操作】都是查询，但是难免需要进行更新【update】 插入【insert】等操作。
# - 所以数据库操作需要经常重复使用，所以要封装。== 函数 或者 类 ？
# - 因为可能一个数据库操作里会有多个方法【增加 修改 查询等方法】，就应该封装类。

import pymysql
import allure
from pymysql.cursors import DictCursor
from tools.handle_logs import logger
@allure.step("数据库查询的操作")
class HandleMysql:
    # 在init函数里定义了两个实例属性： conncursor，后续的实例方法可以共享调用
    def __init__(self,user,password,database,port,host):
        # 第一步：连接上数据库  获取连接对象
        self.connect = pymysql.connect(
            user=user,
            password=password,
            database=database,
            port=port,
            host=host,
            charset="utf8mb4",  # 支持中文字符集
            cursorclass=DictCursor # 读取的数据默认变成字典的格式
        )
        self.cursor = self.connect.cursor()
        # 第二步：需要获取游标，存在一个变量里。

    # 第三步：真正执行sql语句 获取需要的执行结果：
    def quey_data(self,query_sql,match_num=1,size=None):
        """
                1、先执行sql，得到结果数量
                2、获取具体数据之前，如果数量为0 没有必要获取；如果数量>0,才去获取详细数据
                3、获取具体数据： 一条 多条 所有，谁决定获取多少数据？== 由调用方法的用户决定，那么让用户传参。
                 - 可以定义一个参数：match_num =1,fetchone，match_num=2，fetchmany，match_num=-1，fetchall 【参考Jmeter】
                 - 因为更多的场景是查询一条数据，所以match_num设置默认值为1 【默认参数】，用户可以不传
                4、关闭数据库操作： conn  cursor
                  - 这个不管数据库是否出错了 都要执行关闭操作，这个执行代码应该放在finally里。
                :return:
               """
        try:
            result = self.cursor.execute(query_sql)  # 用游标对象调用执行sql的方法.
            logger.info(f"查询结果的条数{result}")   # 获取结果数量，只有数量>0 才去查询具体的数据
            if result >0:
                if match_num==1:
                    data = self.cursor.fetchone()
                    logger.info(f"数据库查询的结果是{data}")
                    return data
                elif match_num==2:
                    data = self.cursor.fetchmany(size=2)
                    logger.info(f"数据库查询结果是{data}")
                    return data
                elif match_num==3:
                    data = self.cursor.fetchall()
                    logger.info(f"数据库查询的结果是{data}")
                    return data

            else:
                logger.info(f"数据库没有查询到任何结果")
        except Exception as err:
            logger.error(f"数据库操作异常{err}")

        finally:# 不管数据库是否异常，都要关闭。
            self.cursor.close()
            self.connect.close()

if __name__ == '__main__':
    from data.my_db import my_db
    data = HandleMysql(**my_db)
    # (** my_db)是 Python中的关键字参数解包操作，作用是将字典 my_db
    # 中的键值对 “拆包” 后，作为关键字参数传递给HandleMysql类的构造方法。
    sql = 'select user_phone,mobile_code from tz_sms_log where user_phone = "13100009999" order by rec_date desc;'
    print(data.quey_data(sql))

# 总结

# 有两个方法所以需要封装成类
# ①数据库连接方法
# ②游标获取方法
# 注意字典转换格式

# 数据库查询方法
# 关闭数据库连接
# 关闭游标连接


